#include "CPrimSphere.h"
#include <math.h>
#include "polynomial.h"

CPrimSphere::CPrimSphere(float radius):IPrimitive(), m_radius(radius)
{}

CPrimSphere::~CPrimSphere(){}



bool CPrimSphere::intersect(const CRay& ray, float& dist, bool useGlobalCoordinates)
{
	//change the coordinate system
	CRay r;
	if (useGlobalCoordinates)
		globalToLocal(ray, r);
	else
		convertRayToLocalCoordinates(ray, r);

	//polynomial coefficient
	float A = r.getDirection().norme2();
	float B = 2 * r.getDirection().dot(r.getOrigin());
	float C = r.getOrigin().norme2() - m_radius * m_radius;

	//get solutions
	Polynomial poly(A, B, C);
	double solutions[2] = {0, 0};
	poly.SolveQuadratic(solutions);

	//no solutions
	if(solutions[0] <= 0 && solutions[1] <= 0)
		return false;

	//get the minimal solution
	else if(solutions[0] <= 0)
		dist = (float)solutions[1];
	else if (solutions[1] <= 0)
		dist = (float)solutions[0];
	else
	{
		if(solutions[0] < solutions[1])
			dist = (float)solutions[0];
		else
			dist = (float)solutions[1];
	}
	return true;
}

/*
To calculate the normal of a sphere, we use the gradient of the sphere's equation
Equation : x^2 + y^2 + z^2 - r^2 = 0
Gradiant = 2x + 2y + 2z
We can simplify it by two to have N(x, y, z)
*/
void CPrimSphere::computeNormal(const CRay& ray, float dist, CVector3f& normal)const
{
	//change the coordinate system
	CRay r;
	convertRayToLocalCoordinates(ray, r);

	//calculate the normal
	normal = r.getOrigin() + r.getDirection()*dist;
	normal.normalize();

	//if we are inside, reverse the normal
	if(r.getDirection().dot(normal)>0)
		normal = normal * -1;

	//reset to the global coordinate systeme
	convertNormalToGlobalCoordinates(normal, normal);

}

bool CPrimSphere::isInside(const CVector3f& p)const
{
	//convert p to local coordinates
	CVector3f localP;
	convertPointToLocalCoordinates(p, localP);

	if(localP.norme2() <= m_radius * m_radius)
		return true;

	return false;
}
